// types/global.d.ts

import type { MetaMaskInpageProvider } from '@metamask/providers';

// 扩展 Window 类型，让 TS 识别
declare global {
    interface Window {
        ethereum?: MetaMaskInpageProvider | undefined;
    }
}

import {
    VotingAddress,
    VotingAddressABI,
    handleNetworkSwitch,
    CONTRACT_OWNER,
} from "./constants";
import React, { useState, Context, FC } from "react";

import { ethers } from "ethers";
import axios from "axios";
import { JWT } from "./JWT";
import type {
    ICandidateForm,
    IfromInput,
    VotingContextValue,
} from "../types/voter.d.ts";
import { votingContextInit } from "./model/voter";
// 定义创建合约需要的入参类型 

export const fetchContract = (signerOrProvider: ethers.Provider | ethers.Signer) =>
    new ethers.Contract(VotingAddress, VotingAddressABI, signerOrProvider);





export const VotingContext: Context<VotingContextValue> = React.createContext<VotingContextValue>(votingContextInit);

// 投票人数据类型
export interface Voter {
    voterID: number;
    name: string;
    image: string;
    voterVote: number;
    ipfs: string;
    address: string;
    votingStatus: boolean;
};

// 候选人数据类型
export interface Candidate {
    age: string;
    name: string;
    candidateID: number;
    image: string;
    totalVote: number;
    ipfs: string;
    address: string;
};
export type VotingProviderProps = {
    children: React.ReactNode;
};
export const VotingProvider: FC<VotingProviderProps> = ({ children }) => {
    const [currentAccount, setCurrentAccount] = useState<string>('');
    const [candidateLength, setCandidateLength] = useState<number>(0);
    const [loader, setLoader] = useState(false);
    const pushCandidate: any[] = [];
    const [candidateArray, setCandidateArray] = useState<Candidate[]>([]);
    const [error, setError] = useState<string>("");
    const [voterArray, setVoterArray] = useState<Voter[]>([]);
    const [voterLength, setVoterLength] = useState<number>(0);
    const [voterAddress, setVoterAddress] = useState<string[]>([]);


    // 检查是否链接钱包
    const checkIfWalletIsConnected = async (): Promise<string | undefined> => {
        if (!window.ethereum) {
            setError("Please install MetaMask");
            return
        }
        await handleNetworkSwitch();
        const accounts = await window.ethereum.request({ method: 'eth_accounts' }) as string[]
        if (accounts.length) {
            setCurrentAccount(accounts[0]);
            return accounts[0];
        } else {
            setError("Please Install MetaMask & Connect, Reload");
        }
    }


    const connectWallet = async () => {

        if (!window.ethereum) {
            return alert("Please install MetaMask");
        };
        const network = await handleNetworkSwitch();
        const accounts = await window.ethereum.request({
            method: "eth_requestAccounts",
        }) as string[];
        setCurrentAccount(accounts[0]);
        setError("");
    }

    // 上传ipfs
    const uploadToIPFS = async (file: File) => {
        if (file) {
            try {
                const address = await checkIfWalletIsConnected();
                if (address as string) {
                    setLoader(true);
                    const formData = new FormData();
                    formData.append("file", file);
                    formData.append("network", "public");
                    const response = await axios({
                        method: "post",
                        url: "https://api.pinata.cloud/pinning/pinFileToIPFS",
                        data: formData,
                        headers: {
                            "Authorization": `Bearer ${JWT}`,
                            "Content-Type": "multipart/form-data",

                        },
                    });
                    console.log("uploadToIPFS", response.data);
                    const imgHash = `https://gateway.pinata.cloud/ipfs/${response.data.IpfsHash}`
                    setLoader(false)
                    return imgHash;
                } else {
                    setLoader(false);
                    console.log('Kindly connect to your wallet')
                }

            } catch (error) {
                console.log("Unable to upload image to Pinata");
                setLoader(false);
            }
        }
    }

    const uploadToIPFSCandidate = async (file: File) => {
        if (file) {
            try {
                setLoader(true);
                const formData = new FormData();
                formData.append("file", file);
                formData.append("network", "public");
                const response = await axios({
                    method: "post",
                    url: "https://api.pinata.cloud/pinning/pinFileToIPFS",
                    data: formData,
                    headers: {
                        "Authorization": `Bearer ${JWT}`,
                        "Content-Type": "multipart/form-data",
                    },

                });
                console.log("response", response);
                const ImgHash = `https://gateway.pinata.cloud/ipfs/${response.data.IpfsHash}`
                setLoader(false);
                return ImgHash;
            } catch (error) {
                setLoader(false)
                console.log("Unable to upload image to Pinata");
            }
        }
    };

    const createVoter = async (formInput: IfromInput, fileUrl: string): Promise<void> => {

        try {
            // fileUrl 表示的是上传的图片
            // url 表示 上传的data的表单数据，有四个属性：name,address,position,image
            console.log('file', fileUrl)
            const { name, address, position } = formInput;
            const connectAddress = await checkIfWalletIsConnected() as string;
            if (connectAddress == CONTRACT_OWNER as any) {

                return setError("Only Ower Of Contract Can Create Voter");

            }

            if (!name || !address || !position) {
                return setError("Input Data is missing");
            }
            setLoader(true);

            const provider = new ethers.BrowserProvider(window.ethereum as ethers.Eip1193Provider);
            const signer = await provider.getSigner();
            const contract = fetchContract(signer);
            const data = JSON.stringify({ name, address, position, image: fileUrl })
            const response = await axios({
                method: "POST",
                url: "https://api.pinata.cloud/pinning/pinJSONToIPFS",
                data: data,
                headers: {
                    Authorization: `Bearer ${JWT}`,
                    "Content-Type": "application/json",
                },
            });
            const url = `https://gateway.pinata.cloud/ipfs/#https://gateway.pinata.cloud/ipfs/${response.data.IpfsHash}`
            const voter = await contract.voterRight(address, name, url, fileUrl, {
                gasLimit: ethers.toBeHex(8000000),
            })
            await voter.wait();
            setLoader(false);
            window.location.href = "/voterList";


        } catch (error) {
            setLoader(false);
            setError("error: Check your API key and data ")
        }
    };

    const getAllVoterData = async (): Promise<void> => {
        try {
            const address = await checkIfWalletIsConnected();
            if (address) {
                const provider = new ethers.BrowserProvider(window.ethereum as ethers.Eip1193Provider);
                const signer = await provider.getSigner();
                const contract = fetchContract(signer);

                const voterListData = await contract.getVoterList();
                setVoterAddress(voterListData);

                const items = await Promise.all(
                    voterListData.map(async (el: string) => {
                        const singleVoterData = await contract.getVoterData(el);
                        console.log(singleVoterData)
                        const singleCandidateData = await contract.getCandidateData(el);
                        console.log('sd', singleCandidateData)
                        // 安全断言，确保singleCandidateData是数组且长度足够
                        if (!Array.isArray(singleCandidateData) || singleCandidateData.length < 7) {
                            throw new Error('合约返回数据格式错误');
                        }
                        return {
                            voterID: Number(singleVoterData[0]),
                            name: singleVoterData[1] as string,
                            image: singleVoterData[4] as string,
                            voterVote: Number(singleVoterData[5]),
                            ipfs: singleVoterData[2] as string,
                            address: singleVoterData[3] as string,
                            votingStatus: singleVoterData[6] as boolean,
                        }
                    })
                );

                setVoterArray(items);


                const voterList = await contract.getVoterLength() as bigint;
                setVoterLength(Number(voterList));

            } else {
                setError("Connect to wallet")
            }
        } catch (error) {
            console.error(error);
            setError("Something went wrong")
        }
    };

    const giveVote = async (id: { address: string; id: number }): Promise<void> => {
        try {
            console.log("id", id);
            const connectAddress = await checkIfWalletIsConnected();
            if (connectAddress == CONTRACT_OWNER as any) {
                return setError("Owner Can not give vote")
            }

            setLoader(true);
            const voterAddress = id.address;
            const voterId = id.id;
            const provider = new ethers.BrowserProvider(window.ethereum as ethers.Eip1193Provider);
            const signer = await provider.getSigner();
            const contract = fetchContract(signer);
            const voteredList = await contract.vote(voterAddress, voterId, {
                gasLimit: ethers.toBeHex(8000000),
            });
            await voteredList.wait();
            setLoader(false);
            window.location.reload(); //  TODO:最好做到无感刷新
        } catch (error) {
            console.error(error);
            setError("Sorry!,You have already voted, Reload Browser");
            setLoader(false)
        }
    };

    const setCandidate = async (candidateForm: ICandidateForm, fileUrl: string, router: any) => {
        const { name, address, age } = candidateForm;

        const connectAddress = await checkIfWalletIsConnected();
        if (connectAddress == CONTRACT_OWNER as any) {
            return setError("only Ower Of Contract Can Create Candidate")
        }

        try {
            if (!name || !age || !address) {
                return console.log("Data Missing")
            }
            setLoader(true)
            const provider = new ethers.BrowserProvider(window.ethereum as ethers.Eip1193Provider);
            const signer = await provider.getSigner();
            const contract = fetchContract(signer);
            console.log("contract", contract)

            const data = JSON.stringify({
                name,
                address,
                image: fileUrl,
                age,
            });
            const response = await axios({
                method: "POST",
                url: "https://api.pinata.cloud/pinning/pinJSONToIPFS",
                data,
                headers: {
                    "Authorization": `Bearer ${JWT}`,
                    "Content-Type": "application/json",
                }
            })
            console.log('res', response);
            const url = `https://gateway.pinata.cloud/ipfs/${response.data.IpfsHash}`
            const candiate = await contract.setCandidate(
                address,
                age,
                name,
                fileUrl,
                url,
                {
                    gasLimit: ethers.toBeHex(8000000),
                }
            );
            await candiate.wait();
            setLoader(false);
            window.location.href = '/';
        } catch (error) {
            setLoader(false);
            setError("Something went wrong ,check your API Key");
        }

    };

    const getNewCandidate = async () => {
        const address = await checkIfWalletIsConnected();
        if (address) {
            const provider = new ethers.BrowserProvider(window.ethereum as ethers.Eip1193Provider);
            const signer = await provider.getSigner();
            const contract = fetchContract(signer);

            const allCandidate = await contract.getCandidate();
            const items = await Promise.all(
                allCandidate.map(async (el: string) => {

                    const singleCandidateData = await contract.getCandidateData(el) as any[];

                    // 安全断言，确保singleCandidateData是数组且长度足够
                    if (!Array.isArray(singleCandidateData) || singleCandidateData.length < 7) {
                        throw new Error('合约返回数据格式错误');
                    }
                    return {
                        age: singleCandidateData[0],
                        name: singleCandidateData[1] as string,
                        candidateID: Number(singleCandidateData[2]),
                        image: singleCandidateData[3],
                        totalVote: Number(singleCandidateData[4]),
                        ipfs: singleCandidateData[5],
                        address: singleCandidateData[6],
                    }
                })
            )
            console.log(items, 'items');
            setCandidateArray(items);

            const allCandidateLength = await contract.getCandidateLength();
            console.log(' allCandidateLength', allCandidateLength, typeof allCandidateLength)
            setCandidateLength(Number(allCandidateLength));
        } else {
            setError("Connect to wallet")
        }
    };

    return (
        <VotingContext.Provider
            value={{
                currentAccount,
                connectWallet,
                uploadToIPFS,
                createVoter,
                setCandidate,
                getNewCandidate,
                giveVote,
                pushCandidate,
                candidateArray,
                uploadToIPFSCandidate,
                getAllVoterData,
                voterArray,
                checkIfWalletIsConnected,
                error,
                candidateLength,
                voterLength,
                loader,
            }}
        >
            {children}
        </VotingContext.Provider>
    )
}